#!/usr/bin/env python

"""
An echo server that uses threads to handle multiple clients at a time.
Entering any line of input at the terminal will exit the server.
"""

import select
import socket
import sys
from threading import *
import getopt
import os


m_cli_list=Lock()
m_cli_dict=Lock()
m_thread_list=Lock()

global_cli_list=[]
global_cli_dict={}
global_thread_list=[]

class Server:
    def __init__(self,myport):
        self.host = ''
        self.port = myport
        self.backlog = 5
        self.size = 1024
        self.server = None

    
    def open_socket(self):
        try:
            self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.server.bind((self.host,self.port))
            self.server.listen(5)
            print "Server Now Started" 
            print "Listening for connections on port %d" %(self.port)
        except socket.error, (value,message):
            if self.server:
                self.server.close()
            print "Could not open socket: " + message
            sys.exit(1)

    
    def exit_all(self):
        print "Exit All"
        m_cli_dict.acquire()
        for (cliname,clisock) in global_cli_dict.items():
            print "Sending exit command to client: ",cliname
            clisock.send('\\exit')
        m_cli_dict.release()
        self.server.shutdown(socket.SHUT_RDWR)
        self.server.close()
        sys.exit()
        
    def run(self):
        self.open_socket()
        input = [self.server,sys.stdin]
        running = 1
        while running:
            inputready,outputready,exceptready = select.select(input,[],[])

            for s in inputready:

                if s == self.server:
                    # handle the server socket
                    print "[Server Thread] Accept Connection"
                    c = Client(self.server.accept())
                    c.start()
                    global_thread_list.append(c)
                    print "DONE"
                elif s == sys.stdin:
                    # handle standard input
                    data = sys.stdin.readline()
                    data=data.replace('\n','')
                    data=data.replace(' ','')
                    print data
                    if(data == '\\exit'):
                        print "NOW EXIT"
                        self.exit_all()
                        running = 0 
        # close all threads

        print "Waitin for thread close"
        self.server.shutdown(socket.SHUT_RDWR)
        self.server.close()
        if(len(global_thread_list) !=0):
            for c in global_thread_list:
                c.join()
                
        print "Server Exit"
        
class Client(Thread):
    def __init__(self,(client,address)):
        self.thread=Thread.__init__(self)
        self.clientsock = client
        
        
        self.address = address
        self.size = 1024
        self.cliname=""

        
    def send_to_one(self,rc_str):
        split_data=[]
        send_to=""
        data_to=""
        
        split_data=rc_str.split(':',1)

        sent_to = split_data[0]
        data_to=split_data[1]
        
        dest = sent_to.replace('\n','')
        dest = dest.replace('@','')
        dest = dest.replace('\\','')
        data = data_to.replace('\n','')
        
        if (global_cli_dict.has_key(dest)):
            print "User Found"
            sendstr="\t\t(PRIVATE CHAT:"+self.cliname+") > "+data
            privsock = global_cli_dict[dest]
            privsock.send(sendstr)
        else:
            print "User not found"
            sendstr="[SERVER] "+dest+" is not found in the chat room " 
            self.cliensock.send(sendstr)

    def send_to_all(self,sendstr):
        print sendstr
        m_cli_list.acquire()
        for othersock in global_cli_list:
            if(self.clientsock != othersock):
                othersock.send(sendstr)
        m_cli_list.release()

    def remove_entry(self):
        sendstr="***"+self.cliname+" has left the chat*** "
        self.send_to_all(sendstr)
        
        m_cli_dict.acquire()
        del global_cli_dict[self.cliname]
        m_cli_dict.release()

        m_cli_list.acquire()
        global_cli_list.remove(self.clientsock)
        m_cli_list.release()
        
        m_thread_list.acquire()
        global_thread_list.remove(self)
        m_thread_list.release()
        
        self.clientsock.close()
        print "Removed Entry for %s from dict" %(self.cliname)
        print global_cli_dict

    def run(self):
        waiting = 1
        """Wait for Cli Name"""
        while waiting:
            data = self.clientsock.recv(self.size)
            if data[0] == '#':
                print "Got Client Name for registration: ",data
                self.cliname=data[1:]
                
                m_cli_list.acquire()
                global_cli_list.append(self.clientsock)
                m_cli_list.release()

                m_cli_dict.acquire()
                global_cli_dict[self.cliname]=self.clientsock
                m_cli_dict.release()
                
                self.clientsock.send('done')
                
                sendstr="***"+self.cliname+" has joined the chat*** "
                self.send_to_all(sendstr)
                
                waiting=0
                break
            else:
                print "Got something else :",data[0],":"
        
        running=1
        
        while running:
            data = self.clientsock.recv(self.size)
                
            if data:
                if(("\exit" in data) and (data[0] == '\\')):
                    self.remove_entry()
                    running=0
                    break;
                elif((data[0]=='\\') and (data[1] == '@') and (':' in data)):
                    self.send_to_one(data)
                else:
                    sendstr="\t\t("+self.cliname+") > "+data
                    self.send_to_all(sendstr)
            else:
                self.remove_entry()
                running = 0
                
        print "Thread Exit", currentThread()
        sys.exit()
        #mythread=threading.currentThread()
        #threading.mythread.exit()
        
              
            
            
            
def mygetopt(myargv):
    
    if(len(myargv) > 0):
            opt,args = getopt.getopt(myargv[1:],"h:",['port='])	
            for option,value in opt:
                    if option == "-h":
                            usage()
                    elif option in "--port":
                            myport=value
                    else:
                            print "Unhandled option"
                            usage()
    else:
            usage()

    return int(myport)



def clrscr():
	"""This function clears the screen according to the OS it is on."""
	if os.name == "posix":
		os.system('clear')
	else:
		os.system('CLS')

def main():
    clrscr()
    myport=mygetopt(sys.argv)
    s = Server(myport)
    s.run()
    
if __name__ == "__main__":
    main()
